#ifndef VTKEXPORTFUNCTIONS_HPP
#define VTKEXPORTFUNCTIONS_HPP

#include "solverBaseTypes_fwd.hpp"
#include "functionParserWrapper.hpp"
#include "tinyxml2.h"
#include "streamAccess.hpp"
#include "meshTypes_fwd.hpp"
#include <Eigen/Dense>
#include <filesystem>

namespace hms::vtk {

namespace xml = tinyxml2;

namespace fs = std::filesystem;

std::stringstream getConverter();

template<typename Derived>
void matrixToText( xml::XMLElement* el,const Eigen::DenseBase<Derived>& matrix);

xml::XMLElement* createDataArray(
	xml::XMLElement* parent, std::string&& type = "Float64"
);

/**
 * @brief Takes an XML document and writes it to the specified ostream.
 * 
 * @param doc 
 * @param os 
 */
void toStream( const xml::XMLDocument* doc, std::ostream& os );

/**
 * @brief Takes an XML document and writes it to the specified path by chaining
 * to toStream().
 * 
 * @param doc 
 * @param filePath 
 */
void toFile( const xml::XMLDocument* doc, const fs::path& filePath );

/**
 * @brief Takes a VTK XML document of a mesh and writes it to the specified path.
 * The file extension is adjusted according to the mesh type held by the
 * MeshVariant parameter.
 * 
 */
void toFile( const xml::XMLDocument*, const fs::path&, const MeshVariant&);

template<typename MeshType>
fs::path& replaceExtension( fs::path& path );

template<typename MeshType>
fs::path& replaceExtension( fs::path& path, const MeshBase<MeshType>& );

/**
 * @brief Each mesh type has a specific representation in VTK, which VTK 
 * identifies by the file extension. This function takes a file path and a mesh
 * variant and replaces the extension with the one appropriate for the mesh type
 * held by the mesh variant.
 * 
 * @return fs::path& with the extension corresponding to the mesh type
 */
fs::path& replaceExtension( fs::path&, const hms::MeshVariant& );

// /**
//  * this one can easily fail without warning, when you expect the path to 
//  * be modified, but it isn't, and the return value is discarded.
//  * @overload
//  */
// fs::path replaceExtension( const fs::path& path, const hms::MeshVariant& meshVar );



xml::XMLElement* createRootNode( xml::XMLDocument* doc, const char* name );

/**
 * @brief Takes an XMLDocument* and creates the declaration and VTK node to it.
 * These are somewhat intertwined and therefore are done in the same function.
 * 
 * @return xml::XMLElement* pointing to the VTK node
 */
xml::XMLElement* createVtkNode( xml::XMLDocument* );

/**
 * @brief Takes an XMLDocument* or XMLElement*, which both implicitly convert
 * to XMLNode*, and, depending on which one of the types is passed in, creates
 * a root node or child node with the passed in name. If a root node is created,
 * an xml declaration is also written.
 * 
 * @param name 
 * @return xml::XMLElement* 
 */
xml::XMLElement* createRootOrInsert( xml::XMLNode*, const char* name );

/**
 * @brief Similar to createRootOrInsert( XMLNode*, const char* ). Here,
 * additional tests are performed to check whether the requested node already
 * exists. If so, the existing node is returned. If not, it is created and 
 * then returned.
 * 
 */
xml::XMLElement* getRootOrInsert( xml::XMLNode* node, const char* name );

xml::XMLElement* findOrInsert( xml::XMLElement*, const char* name );

template<typename T>
xml::XMLElement* findChildWithAttributeOrInsert(
	xml::XMLElement* parent,
	const char* childName,
	const char* attributeName,
	T&& attributeValue
);


void writeFunction(
	xml::XMLElement* parent,
	const std::string& functionString,
	Index component
);

void writeFunction(
	xml::XMLElement* parent,
	const FunctionParserWrapper&,
	Index component
);

void writeFunctions(
	xml::XMLElement* parent,
	const std::vector<FunctionParserWrapper>& fparsers
);

/* e.g. for boundary patches */
void writeTimeSeries(
	xml::XMLElement* parent,
	const TimeSeries& timeSeries
);

/* e.g. for source fields */
template<Uniformity isUniform, typename MeshType>
void writeTimeSeries(
	xml::XMLElement* parent,
	const TimeSeries& timeSeries,
	[[maybe_unused]] const MeshBase<MeshType>& mesh
);



void createBindingDoc( xml::XMLDocument* );

std::unique_ptr<xml::XMLDocument> createBindingDoc();


void updateBindingFile(
	xml::XMLDocument* bindingFile,
	const fs::path& bindingFilePath,
	const Time& time,
	const fs::path& vtkFilePath
);


} // end namespace hms::vtk

#include "vtkFunctions_export.tpp"

#endif